package com.tyros.storage.tool;

import cn.hutool.core.io.FileUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 文件分片工具类
 *
 * @author zhangshuaiyin
 * @date 2022/3/11 15:16
 */
public class FileSplitTools {
    /**
     * 分片文件存储目录
     */
    public static final String PART_DIR = "E:\\resource\\part\\";
    /**
     * 分片文件后缀
     */
    public static final String FILE_TYPE_PART = ".part";

    /**
     * 拆分文件
     *
     * @param filePath 待拆分的完整文件路径
     * @param partSize 按多少字节大小拆分
     * @return 拆分后的文件名列表
     */
    public static List<String> split(String filePath, int partSize) {
        List<String> parts = new ArrayList<>();
        File file = new File(filePath);

        int count = (int) Math.ceil(file.length() / (double) partSize);
        int countLen = (count + "").length();
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                count,
                count * 3,
                1L, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(count * 2),
                new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < count; i++) {
            // test.png.001.part
            String partFileName = PART_DIR + file.getName() + "." + StringUtils.leftPad((i + 1) + "", countLen, '0') + FILE_TYPE_PART;
            threadPool.execute(new SplitRunnable(partSize, i * partSize, partFileName, file));
            parts.add(partFileName);
        }
        threadPool.shutdown();
        return parts;
    }

    /**
     * 左填充
     *
     * @param str    要填充的字符串
     * @param length 填充长度
     * @param ch     填充字符
     * @return 填充后满足长度的字符串
     */
    public static String leftPad(String str, int length, char ch) {
        if (str.length() >= length) {
            return str;
        }
        char[] chs = new char[length];
        Arrays.fill(chs, ch);
        char[] src = str.toCharArray();
        System.arraycopy(src, 0, chs, length - src.length, src.length);
        return new String(chs);
    }

    /**
     * 分割处理Runnable
     */
    private static class SplitRunnable implements Runnable {
        int byteSize;
        String partFileName;
        File originFile;
        int startPos;

        public SplitRunnable(int byteSize, int startPos, String partFileName, File originFile) {
            this.startPos = startPos;
            this.byteSize = byteSize;
            this.partFileName = partFileName;
            this.originFile = originFile;
        }

        @Override
        public void run() {
            try (RandomAccessFile rFile = new RandomAccessFile(originFile, "r");
                 OutputStream os = new FileOutputStream(partFileName)) {
                byte[] b = new byte[byteSize];
                // 移动指针到每“段”开头
                rFile.seek(startPos);
                int s = rFile.read(b);
                os.write(b, 0, s);
                os.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        FileUtil.del(PART_DIR);
        FileUtil.mkdir(PART_DIR);

        List<String> split = FileSplitTools.split("E:\\resource\\test.jpg", 100 * 1024);
        split.forEach(System.out::println);
    }
}
